package de.bht.fb6.cg1.exercise1;

import java.awt.Color;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * Represents a representation widget for a {@link ColorModelInterface}.
 * <p>
 * It allows syncronisation with other {@link ColorPanel}s.
 *   
 * @author Dominik Eckelmann
 *
 */
@SuppressWarnings("serial")
public class ColorPanel extends JPanel implements ChangeListener, ColorSyncListener {

    /**
     * stores the colormodel to use.
     * 
     * It will handle all conversation stuff
     */
    private final ColorModelInterface model;

    /**
     * all sliders from the widget.
     */
    private final JSlider[] slider;

    /**
     * all value fields from the widger.
     */
    private final JTextField[] values;

    /**
     * all registrated listeners that'll be informed on value change.
     */
    private final Set<ColorSyncListener> listeners;

    private boolean update;

    /**
     * creates a new Widget to a given color model.
     * 
     * @param model color space model to use.
     */
    public ColorPanel(final ColorModelInterface model) {
        final char[] labels = model.getColorLabels();
        this.model = model;
        update = false;

        slider = new JSlider[model.getComponentCount()];
        values = new JTextField[model.getComponentCount()];

        setBorder(javax.swing.BorderFactory.createEtchedBorder());
        setLayout(new GridLayout(3, model.getComponentCount()));

        for (int i = 0; i < labels.length; i++) {
            addColumn(i);
        }

        listeners = new HashSet<ColorSyncListener>();
    }

    /**
     * Creates a new Column.
     * <p>
     * A Column is a short Label (eg R for red), a Slider to set a new Color and a textfield for a new value.
     * 
     * @param number    number of the column
     */
    private void addColumn(final int number) {
        add(new JLabel("" + model.getColorLabels()[number]));

        slider[number] = new JSlider(model.getComponentRange()[number].getMin(), model.getComponentRange()[number]
                .getMax(), model.getModelColor()[number]);
        add(slider[number]);
        slider[number].addChangeListener(this);

        values[number] = new JTextField();
        add(values[number]);
        values[number].setText("" + model.getModelColor()[number]);
        values[number].setEnabled(false);

    }

    /**
     * On Value change in a slider we figure the new value out and set it.
     * <p>
     * aditionaly we send a event to all {@link ColorPanel#listeners}
     * 
     * @param e     event throwen from a {@link JSlider}
     */
    @Override
    public void stateChanged(final ChangeEvent e) {
        final int[] newColor = new int[slider.length];
        for (int i = 0; i < slider.length; i++) {
            newColor[i] = slider[i].getValue();
            values[i].setText("" + newColor[i]);
        }
        model.setModelColor(newColor);
        fireColorSyncEvent();
    }

    /**
     * send a {@link ColorSyncEvent} with the current color to all listeners.
     */
    private void fireColorSyncEvent() {
        if (update) {
            return;
        }
        model.getColor();
        final ColorSyncEvent event = new ColorSyncEvent(model.getColor(), this);
        for (final ColorSyncListener listener : listeners) {
            listener.colorChanged(event);
        }
    }

    /**
     * registers a new listener to a {@link ColorSyncEvent}.
     * <p>
     * the event is throwen on a user based value change.
     * 
     * @param listener  the listener itself.
     */
    public void addListener(final ColorSyncListener listener) {
        listeners.add(listener);
    }

    /**
     * applys a new color to the widget.
     * 
     * @param c     the new color.
     */
    public void setColor(final Color c) {
        model.setColor(c);
        update = true;
        update();
        update = false;
    }

    /**
     * updates the graphical components.
     */
    private void update() {
        final int[] newVal = model.getModelColor();
        for (int i = 0; i < model.getComponentCount(); i++) {
            slider[i].setValue(newVal[i]);
            values[i].setText("" + newVal[i]);
        }
    }

    /**
     * handels a color change from another {@link ColorPanel} and applies the 
     * color.
     * @param event     event with the new color informations
     */
    @Override
    public void colorChanged(final ColorSyncEvent event) {
        if (event.getColor().equals(model.getColor())) {
            return;
        }
        setColor(event.getColor());
    }
    
    /**
     * return informations to the current panel
     * @return informational string
     */
    @Override
    public String toString() {
        return "ColorPanel: current colorspace is " + model.getName() +
               " and the current color is " + model.getColor();
    }

    /* Create a hashcode from the ColorPanel
     * 
     * @return numerical hashcode
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((model == null) ? 0 : model.hashCode());
        result = prime * result + Arrays.hashCode(slider);
        result = prime * result + (update ? 1231 : 1237);
        result = prime * result + Arrays.hashCode(values);
        return result;
    }

    /* compares the object with another
     * 
     * @param obj another object to compare
     * @return true when obj is equal to this object
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof ColorPanel)) {
            return false;
        }
        ColorPanel other = (ColorPanel) obj;
        if (listeners == null) {
            if (other.listeners != null) {
                return false;
            }
        } else if (!listeners.equals(other.listeners)) {
            return false;
        }
        if (model == null) {
            if (other.model != null) {
                return false;
            }
        } else if (!model.equals(other.model)) {
            return false;
        }
        if (!Arrays.equals(slider, other.slider)) {
            return false;
        }
        if (update != other.update) {
            return false;
        }
        if (!Arrays.equals(values, other.values)) {
            return false;
        }
        return true;
    }
    
    
}
